﻿using System;
using System.Collections.Generic;
using System.Web.Security;
 
using PagedList;
using System.Data.SqlClient;
using FB.Membership.Settings;
using FB.Helpers;
using System.Web.Configuration;
using FB.Enums;
using FB.Membership.Principal;

namespace FB.Membership
{
	public class AspNetMembershipProviderWrapper : IUserService, IPasswordService
	{
		private readonly MembershipProvider _membershipProvider;
        string _ConnString;

        public static bool NeedChangePWSession
        {
            get
            {
                if (System.Web.HttpContext.Current.Session["NeedChangePWSession"] == null)
                {
                    if (System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
                    {
                        var m = new AspNetMembershipProviderWrapper();
                        System.Web.HttpContext.Current.Session["NeedChangePWSession"] =
                            m.IsEnforcedChangingPassword(System.Web.HttpContext.Current.User.Identity.Name);
                    }
                }
                return (bool)System.Web.HttpContext.Current.Session["NeedChangePWSession"];
            }
            set { System.Web.HttpContext.Current.Session.Add("NeedChangePWSession", value); }
        }

		public AspNetMembershipProviderWrapper()
		{
			_membershipProvider = System.Web.Security.Membership.Provider;
            Initialize();
		}

		public AspNetMembershipProviderWrapper(MembershipProvider membershipProvider)
		{
			_membershipProvider = membershipProvider;
            Initialize();
		}

		public AspNetMembershipProviderSettingsWrapper Settings
		{
			get{ return new AspNetMembershipProviderSettingsWrapper(_membershipProvider); }
		}


        void Initialize()
        {
            var providerSection = System.Configuration.ConfigurationManager.GetSection("system.web/membership") as System.Web.Configuration.MembershipSection;
            if (null != providerSection)
            {
                var p = providerSection.Providers[_membershipProvider.Name];
                var connStrName = p.Parameters["connectionStringName"];
                if (!string.IsNullOrEmpty(connStrName))
                {
                    _ConnString = WebConfigurationManager.ConnectionStrings[connStrName].ConnectionString;
                }
            }
        }

        public void CheckDbConnection()
        {
            if (string.IsNullOrEmpty(_ConnString))
                throw new NotImplementedException("The database connection could not be found.");
        }


        #region Account Type

        public void SetUserType(Guid UserId, UserType UType = UserType.Unknown)
        {
            CheckDbConnection();

            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql_check = @"SELECT COUNT(*) FROM [dbo].[UserType] WHERE UserId=@UserId";
                var sql_add = @"INSERT INTO [dbo].[UserType] (UserId, UserType) VALUES (@UserId, @UserType)";
                var sql_update = @"UPDATE [dbo].[UserType] SET UserType=@UserType WHERE UserId=@UserId";
                var Counter = 0;

                using (var command = new SqlCommand(sql_check, SqlConn))
                {
                    command.Parameters.Add(new SqlParameter("@UserID", UserId));
                    command.Connection.Open();
                    Counter = (int)command.ExecuteScalar();
                    command.Connection.Close();
                }


                if (Counter > 0)
                {
                    using (var command = new SqlCommand(sql_update, SqlConn))
                    {
                        command.Parameters.Add(new SqlParameter("@UserID", UserId));
                        command.Parameters.Add(new SqlParameter("@UserType", UType));
                        command.Connection.Open();
                        command.ExecuteNonQuery();
                        command.Connection.Close();
                    }
                }
                else
                {
                    using (var command = new SqlCommand(sql_add, SqlConn))
                    {
                        command.Parameters.Add(new SqlParameter("@UserID", UserId));
                        command.Parameters.Add(new SqlParameter("@UserType", UType));
                        command.Connection.Open();
                        command.ExecuteNonQuery();
                        command.Connection.Close();
                    }
                }
            }
        }

        public UserType GetUserType(Guid UserId)
        {
            // default type
            var result = UserType.Unknown;

            CheckDbConnection();
            
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"SELECT UserId, UserType FROM [dbo].[UserType] WHERE UserId = @UserId";

                using (var command = new SqlCommand(sql, SqlConn))
                {
                    command.Parameters.Add(new SqlParameter("@UserId", UserId));

                    command.Connection.Open();
                    using (var reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
                    {
                        if (reader.Read())
                            Enum.TryParse<UserType>(reader["UserType"].ToString(), out result);

                        if (reader.Read())
                            throw new System.Data.DataException("multiple rows returned from query");
                    }
                }
            }

            return result;
        }

        #endregion


        #region Apps Service Extension


        public string GetPassword(string userID)
        {
            CheckDbConnection();

            string password = null;

            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"SELECT Password FROM [dbo].[aspnet_membership] WHERE UserId = @UserId AND PasswordFormat = 0";

                using (var command = new SqlCommand(sql, SqlConn))
                {
                    command.Parameters.Add(new SqlParameter("@UserId", userID));

                    command.Connection.Open();
                    using (var reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
                    {
                        if (reader.Read())
                        {
                            password = (string)reader["Password"];
                        }
                    }
                }
            }

            return password;
        }



        public List<string> GetService(string userName)
        {
            CheckDbConnection();
            var result = new List<string>();
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"SELECT US.UserId, U.UserName, AppsId FROM [dbo].[UsersInServices] US JOIN [dbo].[aspnet_Users] U ON U.UserId = US.UserId WHERE U.UserName = @UserName";

                using (var command = new SqlCommand(sql, SqlConn))
                {
                    command.Parameters.Add(new SqlParameter("@UserName", userName));

                    command.Connection.Open();
                    using (var reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
                    {
                        while (reader.Read())
                        {
                            result.Add((string)reader["AppsId"]);
                        }
                    }
                }
            }

            return result;
        }


        public List<string> FindUsersByService(ApplicationKey AppName)
        {
            CheckDbConnection();

            var users = new List<string>();

            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"SELECT US.UserId, U.UserName, AppsId FROM [dbo].[UsersInServices] US JOIN [dbo].[aspnet_Users] U ON U.UserId = US.UserId WHERE AppsId = @AppsID";

                using (var command = new SqlCommand(sql, SqlConn))
                {
                    command.Parameters.Add(new SqlParameter("@AppsID", AppName.ToString()));

                    command.Connection.Open();
                    using (var reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
                    {
                        while (reader.Read())
                        {
                            users.Add((string)reader["UserName"]);
                        }
                    }
                }
            }

            return users;
        }


        public bool AddToService(ApplicationKey AppName, string username)
        {
            var m = Get(username);
            return (null != m) ? AddToService(AppName, (Guid)m.ProviderUserKey) : false;
        }

        public bool AddToService(ApplicationKey AppName, Guid userId)
        {
            CheckDbConnection();
            var affectedRows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"INSERT INTO [dbo].[UsersInServices] (UserId, AppsId)
                            SELECT @UserID, @AppsID
                            WHERE (NOT EXISTS (SELECT * FROM [dbo].[UsersInServices] WHERE (UserId = @UserID) AND (AppsId = @AppsID)))";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserID", userId));
                command.Parameters.Add(new SqlParameter("@AppsID", AppName.ToString()));
                command.Connection.Open();
                affectedRows = command.ExecuteNonQuery();
            }

            return affectedRows > 0;
        }


        public bool RemoveFromService(ApplicationKey AppName, string username)
        {
            var m = Get(username);
            return (null != m) ? RemoveFromService(AppName, (Guid)m.ProviderUserKey) : false;
        }

        public bool RemoveFromService(ApplicationKey AppName, Guid userId)
        {
            CheckDbConnection();
            var affectedRows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"DELETE [dbo].[UsersInServices] WHERE (UserId = @UserID) AND (AppsId = @AppsID)";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserID", userId));
                command.Parameters.Add(new SqlParameter("@AppsID", AppName.ToString()));
                command.Connection.Open();
                affectedRows = command.ExecuteNonQuery();
            }

            return affectedRows > 0;
        }



        public bool HasService(string AppKey, string username, List<string> ServiceList)
        {
            var result = false;
            if (username.Equals(FB.Helpers.AppSettingsBase.SystemAdmin, StringComparison.InvariantCultureIgnoreCase))
            {
                return true;
            }
            if (string.IsNullOrEmpty(AppKey))
            {
                throw new Exception("The Application Key cannot be found.");
            }
            else
            {
                result = ServiceList.Contains(AppKey);
            }
            return result;
        }

        public bool HasService(string AppKey, string username)
        {
            return HasService(AppKey, username, GetService(username));
        }


        public bool SetPasswordChanged(string username)
        {
            CheckDbConnection();
            var Rows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"UPDATE [dbo].[aspnet_Membership] SET EnforcedChangingPassword=0 
                            FROM [dbo].[aspnet_Membership] M 
                            JOIN [dbo].[aspnet_Users] U ON U.UserId = M.UserId
                            WHERE U.LoweredUserName = @UserName";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserName", username.ToLower()));
                command.Connection.Open();
                Rows = command.ExecuteNonQuery();
            }

            if (Rows > 0) AspNetMembershipProviderWrapper.NeedChangePWSession = false;

            return Rows > 0;
        }


        public void ChangePasswordFormat(string username, MembershipPasswordFormat PW)
        {
            CheckDbConnection();
            var Rows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"UPDATE [dbo].[aspnet_Membership] SET PasswordFormat = @PWF 
                            FROM [dbo].[aspnet_Membership] M 
                            JOIN [dbo].[aspnet_Users] U ON U.UserId = M.UserId
                            WHERE U.LoweredUserName = @UserName";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserName", username.ToLower()));
                command.Parameters.Add(new SqlParameter("@PWF", (int)PW));
                command.Connection.Open();
                Rows = command.ExecuteNonQuery();
            }

            //MembershipPasswordFormat.pw
        }

        public bool ForceChangingPassword(string username)
        {
            CheckDbConnection();
            var Rows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"UPDATE [dbo].[aspnet_Membership] SET EnforcedChangingPassword=1 
                            FROM [dbo].[aspnet_Membership] M 
                            JOIN [dbo].[aspnet_Users] U ON U.UserId = M.UserId
                            WHERE U.LoweredUserName = @UserName";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserName", username.ToLower()));
                command.Connection.Open();
                Rows = command.ExecuteNonQuery();
            }

            return Rows > 0;
        }

        public bool IsEnforcedChangingPassword(string username)
        {
            CheckDbConnection();
            var Rows = 0;
            using (var SqlConn = new SqlConnection(_ConnString))
            {
                var sql = @"SELECT COUNT(*) FROM [dbo].[aspnet_Membership] M
                            JOIN [dbo].[aspnet_Users] U ON U.UserId=M.UserId
                            WHERE U.LoweredUserName = @UserName AND M.EnforcedChangingPassword=1";

                var command = new SqlCommand(sql, SqlConn);
                command.Parameters.Add(new SqlParameter("@UserName", username.ToLower()));
                command.Connection.Open();
                Rows = Convert.ToInt32(command.ExecuteScalar());
            }

            return Rows > 0;
        }


        #endregion


        #region IPasswordService Members


        public void Unlock(MembershipUser user)
		{
			user.UnlockUser();
		}

		public string ResetPassword(MembershipUser user)
		{
			return user.ResetPassword();
		}

		public string ResetPassword(MembershipUser user, string passwordAnswer)
		{
			return user.ResetPassword(passwordAnswer);
		}

		public void ChangePassword(MembershipUser user, string newPassword)
		{
			var resetPassword = user.ResetPassword();
			    if(!user.ChangePassword(resetPassword, newPassword))
				throw new MembershipPasswordException("Could not change password.");
		}

		public void ChangePassword(MembershipUser user, string oldPassword, string newPassword)
		{
			if (!user.ChangePassword(oldPassword, newPassword))
				throw new MembershipPasswordException("Could not change password.");


		}

		#endregion

		#region IUserService Members




		public IPagedList<MembershipUser> FindAll(int pageNumber, int pageSize)
		{
			// get one page of users
			int totalUserCount;
            var usersCollection = _membershipProvider.GetAllUsers(pageNumber - 1, pageSize, out totalUserCount);

			// convert from MembershipUserColletion to PagedList<MembershipUser> and return
			var converter = new EnumerableToEnumerableTConverter<MembershipUserCollection, MembershipUser>();
			var usersList = converter.ConvertTo<IEnumerable<MembershipUser>>(usersCollection);
            var usersPagedList = new StaticPagedList<MembershipUser>(usersList, pageNumber, pageSize, totalUserCount);
			return usersPagedList;
		}

        public IPagedList<MembershipUser> FindByEmail(string emailAddressToMatch, int pageNumber, int pageSize)
		{
			// get one page of users
			int totalUserCount;
            var usersCollection = _membershipProvider.FindUsersByEmail(emailAddressToMatch, pageNumber - 1, pageSize, out totalUserCount);

			// convert from MembershipUserColletion to PagedList<MembershipUser> and return
			var converter = new EnumerableToEnumerableTConverter<MembershipUserCollection, MembershipUser>();
			var usersList = converter.ConvertTo<IEnumerable<MembershipUser>>(usersCollection);
            var usersPagedList = new StaticPagedList<MembershipUser>(usersList, pageNumber, pageSize, totalUserCount);
			return usersPagedList;
		}

        public IPagedList<MembershipUser> FindByUserName(string userNameToMatch, int pageNumber, int pageSize)
		{
			// get one page of users
			int totalUserCount;
            var usersCollection = _membershipProvider.FindUsersByName(userNameToMatch, pageNumber - 1, pageSize, out totalUserCount);

			// convert from MembershipUserColletion to PagedList<MembershipUser> and return
			var converter = new EnumerableToEnumerableTConverter<MembershipUserCollection, MembershipUser>();
			var usersList = converter.ConvertTo<IEnumerable<MembershipUser>>(usersCollection);
            var usersPagedList = new StaticPagedList<MembershipUser>(usersList, pageNumber, pageSize, totalUserCount);
			return usersPagedList;
		}

		public MembershipUser Get(string userName)
		{
			return _membershipProvider.GetUser(userName, false);
		}

		public MembershipUser Get(object providerUserKey)
		{
			return _membershipProvider.GetUser(providerUserKey, false);
		}

		public MembershipUser Create(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved)
		{
			return Create(username, password, email, passwordQuestion, passwordAnswer, isApproved, Guid.NewGuid());
		}

		public MembershipUser Create(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey)
		{
			MembershipCreateStatus status;
			var user = _membershipProvider.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
			if(status != MembershipCreateStatus.Success) 
				throw new MembershipCreateUserException(status);
			return user;
		}

		public void Update(MembershipUser user)
		{
			_membershipProvider.UpdateUser(user);
		}

		public void Delete(MembershipUser user)
		{
			_membershipProvider.DeleteUser(user.UserName, false);
		}

		public void Delete(MembershipUser user, bool deleteAllRelatedData)
		{
			_membershipProvider.DeleteUser(user.UserName, deleteAllRelatedData);
		}

		public MembershipUser Touch(MembershipUser user)
		{
			return _membershipProvider.GetUser(user.UserName, true);
		}

		public MembershipUser Touch(string userName)
		{
			return _membershipProvider.GetUser(userName, true);
		}

		public MembershipUser Touch(object providerUserKey)
		{
			return _membershipProvider.GetUser(providerUserKey, true);
		}

		public int TotalUsers
		{
			get
			{
				int totalUsers;
				_membershipProvider.GetAllUsers(1, 1, out totalUsers);
				return totalUsers;
			}
		}

		public int UsersOnline
		{
			get
			{
				return _membershipProvider.GetNumberOfUsersOnline();
			}
		}

		#endregion



	}
}